refactor to subcrates

This commit is contained in:
JT
2021-08-11 06:51:08 +12:00
parent f62e3119c4
commit 1355a5dd33
24 changed files with 296 additions and 257 deletions

View File

@ -0,0 +1,122 @@
use std::{cell::RefCell, rc::Rc};
use nu_parser::{ParserState, ParserWorkingSet, Signature, SyntaxShape};
pub fn create_default_context() -> Rc<RefCell<ParserState>> {
let parser_state = Rc::new(RefCell::new(ParserState::new()));
let delta = {
let parser_state = parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let sig =
Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition");
working_set.add_decl(sig.into());
let sig = Signature::build("if")
.required("cond", SyntaxShape::Expression, "condition")
.required("then_block", SyntaxShape::Block, "then block")
.optional(
"else",
SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)),
"optional else followed by else block",
);
working_set.add_decl(sig.into());
let sig = Signature::build("let")
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("let-env")
.required("var_name", SyntaxShape::String, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::String)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("alias")
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("sum").required(
"arg",
SyntaxShape::List(Box::new(SyntaxShape::Number)),
"list of numbers",
);
working_set.add_decl(sig.into());
let sig = Signature::build("build-string").rest(SyntaxShape::String, "list of string");
working_set.add_decl(sig.into());
let sig = Signature::build("def")
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition");
working_set.add_decl(sig.into());
let sig = Signature::build("for")
.required(
"var_name",
SyntaxShape::Variable,
"name of the looping variable",
)
.required(
"range",
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Int)),
"range of the loop",
)
.required("block", SyntaxShape::Block, "the block to run");
working_set.add_decl(sig.into());
let sig =
Signature::build("benchmark").required("block", SyntaxShape::Block, "the block to run");
working_set.add_decl(sig.into());
// let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'));
// working_set.add_decl(sig.into());
// let sig = Signature::build("bar")
// .named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'))
// .switch("--rock", "rock!!", Some('r'));
// working_set.add_decl(sig.into());
let sig = Signature::build("exit");
working_set.add_decl(sig.into());
let sig = Signature::build("vars");
working_set.add_decl(sig.into());
let sig = Signature::build("decls");
working_set.add_decl(sig.into());
let sig = Signature::build("blocks");
working_set.add_decl(sig.into());
let sig = Signature::build("stack");
working_set.add_decl(sig.into());
let sig = Signature::build("add");
working_set.add_decl(sig.into());
let sig = Signature::build("add it");
working_set.add_decl(sig.into());
let sig = Signature::build("add it together")
.required("x", SyntaxShape::Int, "x value")
.required("y", SyntaxShape::Int, "y value");
working_set.add_decl(sig.into());
working_set.render()
};
{
ParserState::merge_delta(&mut *parser_state.borrow_mut(), delta);
}
parser_state
}

249
crates/nu-cli/src/errors.rs Normal file
View File

@ -0,0 +1,249 @@
use core::ops::Range;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use nu_engine::ShellError;
use nu_parser::{ParseError, ParserWorkingSet, Span};
fn convert_span_to_diag(
working_set: &ParserWorkingSet,
span: &Span,
) -> Result<(usize, Range<usize>), Box<dyn std::error::Error>> {
for (file_id, (_, start, end)) in working_set.files().enumerate() {
if span.start >= *start && span.end <= *end {
let new_start = span.start - start;
let new_end = span.end - start;
return Ok((file_id, new_start..new_end));
}
}
panic!("internal error: can't find span in parser state")
}
pub fn report_parsing_error(
working_set: &ParserWorkingSet,
error: &ParseError,
) -> Result<(), Box<dyn std::error::Error>> {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
let diagnostic =
match error {
ParseError::Mismatch(missing, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Type mismatch during operation")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("expected {}", missing))])
}
ParseError::ExtraTokens(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Extra tokens in code")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("extra tokens")
])
}
ParseError::ExtraPositional(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Extra positional argument")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message("extra positional argument")])
}
ParseError::UnexpectedEof(s, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unexpected end of code")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("expected {}", s))])
}
ParseError::Unclosed(delim, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unclosed delimiter")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("unclosed {}", delim))])
}
ParseError::UnknownStatement(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unknown statement")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("unknown statement")
])
}
ParseError::MultipleRestParams(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Multiple rest params")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message("multiple rest params")])
}
ParseError::VariableNotFound(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Variable not found")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("variable not found")
])
}
ParseError::UnknownCommand(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unknown command")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("unknown command")
])
}
ParseError::UnknownFlag(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unknown flag")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("unknown flag")
])
}
ParseError::UnknownType(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unknown type")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("unknown type")
])
}
ParseError::MissingFlagParam(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Missing flag param")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message("flag missing parameter")])
}
ParseError::ShortFlagBatchCantTakeArg(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Batches of short flags can't take arguments")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message("short flag batches can't take args")])
}
ParseError::MissingPositional(name, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Missing required positional arg")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("missing {}", name))])
}
ParseError::MissingType(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Missing type")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("expected type")
])
}
ParseError::TypeMismatch(ty, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Type mismatch")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("expected {:?}", ty))])
}
ParseError::MissingRequiredFlag(name, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Missing required flag")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("missing required flag {}", name))])
}
ParseError::IncompleteMathExpression(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Incomplete math expresssion")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message("incomplete math expression")])
}
ParseError::UnknownState(name, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unknown state")
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("unknown state {}", name))])
}
ParseError::NonUtf8(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Non-UTF8 code")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("non-UTF8 code")
])
}
};
// println!("DIAG");
// println!("{:?}", diagnostic);
codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?;
Ok(())
}
pub fn report_shell_error(
working_set: &ParserWorkingSet,
error: &ShellError,
) -> Result<(), Box<dyn std::error::Error>> {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
let diagnostic = match error {
ShellError::OperatorMismatch {
op_span,
lhs_ty,
lhs_span,
rhs_ty,
rhs_span,
} => {
let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?;
let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?;
let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?;
Diagnostic::error()
.with_message("Type mismatch during operation")
.with_labels(vec![
Label::primary(op_file_id, op_range).with_message("type mismatch for operator"),
Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()),
Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()),
])
}
ShellError::Unsupported(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Unsupported operation")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("unsupported operation")
])
}
ShellError::InternalError(s) => {
Diagnostic::error().with_message(format!("Internal error: {}", s))
}
ShellError::VariableNotFound(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message("Variable not found")
.with_labels(vec![
Label::primary(diag_file_id, diag_range).with_message("variable not found")
])
}
ShellError::CantConvert(s, span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error()
.with_message(format!("Can't convert to {}", s))
.with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("can't convert to {}", s))])
}
};
// println!("DIAG");
// println!("{:?}", diagnostic);
codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?;
Ok(())
}

7
crates/nu-cli/src/lib.rs Normal file
View File

@ -0,0 +1,7 @@
mod default_context;
mod errors;
mod syntax_highlight;
pub use default_context::create_default_context;
pub use errors::{report_parsing_error, report_shell_error};
pub use syntax_highlight::NuHighlighter;

View File

@ -0,0 +1,92 @@
use nu_ansi_term::Style;
use nu_parser::{FlatShape, ParserState, ParserWorkingSet};
use reedline::{Highlighter, StyledText};
use std::{cell::RefCell, rc::Rc};
pub struct NuHighlighter {
pub parser_state: Rc<RefCell<ParserState>>,
}
impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str) -> StyledText {
let (shapes, global_span_offset) = {
let parser_state = self.parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let (block, _) = working_set.parse_source(line.as_bytes(), false);
let shapes = working_set.flatten_block(&block);
(shapes, parser_state.next_span_start())
};
let mut output = StyledText::default();
let mut last_seen_span = global_span_offset;
for shape in &shapes {
if shape.0.end <= last_seen_span {
// We've already output something for this span
// so just skip this one
continue;
}
if shape.0.start > last_seen_span {
let gap = line
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
.to_string();
output.push((Style::new(), gap));
}
let next_token = line
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string();
match shape.1 {
FlatShape::External => output.push((Style::new().bold(), next_token)),
FlatShape::Garbage => output.push((
Style::new()
.fg(nu_ansi_term::Color::White)
.on(nu_ansi_term::Color::Red)
.bold(),
next_token,
)),
FlatShape::InternalCall => output.push((
Style::new().fg(nu_ansi_term::Color::LightBlue).bold(),
next_token,
)),
FlatShape::Int => {
output.push((Style::new().fg(nu_ansi_term::Color::Green), next_token))
}
FlatShape::Float => {
output.push((Style::new().fg(nu_ansi_term::Color::Green), next_token))
}
FlatShape::Bool => {
output.push((Style::new().fg(nu_ansi_term::Color::LightCyan), next_token))
}
FlatShape::Literal => {
output.push((Style::new().fg(nu_ansi_term::Color::Blue), next_token))
}
FlatShape::Operator => output.push((
Style::new().fg(nu_ansi_term::Color::LightPurple).bold(),
next_token,
)),
FlatShape::Signature => output.push((
Style::new().fg(nu_ansi_term::Color::Green).bold(),
next_token,
)),
FlatShape::String => output.push((
Style::new().fg(nu_ansi_term::Color::Yellow).bold(),
next_token,
)),
FlatShape::Variable => output.push((
Style::new().fg(nu_ansi_term::Color::Blue).bold(),
next_token,
)),
}
last_seen_span = shape.0.end;
}
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
if !remainder.is_empty() {
output.push((Style::new(), remainder));
}
output
}
}