forked from katzen-cafe/iowo
rewrite error system and add evaluator architecture
the evaluator architecture is also only a temporary solution and very flawed but i have no other ideas
This commit is contained in:
parent
0ce869e859
commit
17878b3e87
7 changed files with 278 additions and 145 deletions
89
src/error/mod.rs
Normal file
89
src/error/mod.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use codespan_reporting::{
|
||||||
|
diagnostic::{Diagnostic, Label},
|
||||||
|
files::SimpleFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Span;
|
||||||
|
|
||||||
|
pub struct Errors {
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub locs: Vec<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ErrorKind {
|
||||||
|
InvalidToken,
|
||||||
|
SyntaxError(SyntaxErrorKind),
|
||||||
|
CommandNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SyntaxErrorKind {
|
||||||
|
/// `MissingStreamer` means, that the pipeline starts with a Pipe (`|`), so it has no streamer as input in front of it.
|
||||||
|
MissingStreamer,
|
||||||
|
/// `MissingSink` means, that the pipeline ends with a Pipe (`|`), meaning that the output can't go anywhere
|
||||||
|
MissingSink,
|
||||||
|
/// This indicates a missing filter somewhere in the pipeline, meaning that there's 2 pipes after one another
|
||||||
|
MissingFilter,
|
||||||
|
/// A literal cannot be a sink
|
||||||
|
LiteralAsSink,
|
||||||
|
/// A literal can't be a filter either
|
||||||
|
LiteralAsFilter,
|
||||||
|
/// A literal acting as streamer cannot take arguments
|
||||||
|
LiteralWithArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Errors {
|
||||||
|
pub fn new(kind: ErrorKind, locs: Vec<Span>) -> Self {
|
||||||
|
Self { kind, locs }
|
||||||
|
}
|
||||||
|
pub fn new_single(kind: ErrorKind, loc: Span) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
locs: vec![loc],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn into_diag(
|
||||||
|
&self,
|
||||||
|
file_id: usize,
|
||||||
|
file_db: &SimpleFiles<&str, String>,
|
||||||
|
) -> Diagnostic<usize> {
|
||||||
|
let Errors { kind, locs } = self;
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
ErrorKind::InvalidToken => simple_diag(locs.to_vec(), file_id, "invalid tokens"),
|
||||||
|
ErrorKind::SyntaxError(syntax_error) => match syntax_error {
|
||||||
|
SyntaxErrorKind::MissingStreamer => simple_diag(
|
||||||
|
locs.to_vec(),
|
||||||
|
file_id,
|
||||||
|
"pipeline is missing an input provider",
|
||||||
|
),
|
||||||
|
SyntaxErrorKind::MissingSink => {
|
||||||
|
simple_diag(locs.to_vec(), file_id, "pipeline is missing a sink")
|
||||||
|
}
|
||||||
|
SyntaxErrorKind::MissingFilter => {
|
||||||
|
simple_diag(locs.to_vec(), file_id, "missing filters in pipeline")
|
||||||
|
}
|
||||||
|
SyntaxErrorKind::LiteralAsSink => {
|
||||||
|
simple_diag(locs.to_vec(), file_id, "pipelines can't end in a literal")
|
||||||
|
}
|
||||||
|
SyntaxErrorKind::LiteralAsFilter => {
|
||||||
|
simple_diag(locs.to_vec(), file_id, "literals can't filter data")
|
||||||
|
}
|
||||||
|
SyntaxErrorKind::LiteralWithArgs => {
|
||||||
|
simple_diag(locs.to_vec(), file_id, "literals can't take arguments")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ErrorKind::CommandNotFound => simple_diag(locs.to_vec(), file_id, "command not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simple_diag(spans: Vec<Span>, file_id: usize, msg: &str) -> Diagnostic<usize> {
|
||||||
|
Diagnostic::error().with_message(msg).with_labels(
|
||||||
|
spans
|
||||||
|
.into_iter()
|
||||||
|
.map(|span| Label::primary(file_id, span))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
117
src/evaluator.rs
Normal file
117
src/evaluator.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
use std::{collections::HashMap, process::exit};
|
||||||
|
|
||||||
|
use clap::error::Result;
|
||||||
|
use codespan_reporting::{
|
||||||
|
files::SimpleFiles,
|
||||||
|
term::{
|
||||||
|
self,
|
||||||
|
termcolor::{ColorChoice, StandardStream},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
builtins::initialise_globals,
|
||||||
|
error::{ErrorKind, Errors},
|
||||||
|
syntax::{
|
||||||
|
check::{self, check},
|
||||||
|
parse_syntax, PipelineElement,
|
||||||
|
},
|
||||||
|
typed::into_typed_repr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this is also bad
|
||||||
|
// need a better architecture for this
|
||||||
|
|
||||||
|
pub struct Evaluator<'a> {
|
||||||
|
curr_phase: EvalPhase,
|
||||||
|
files: SimpleFiles<&'a str, String>,
|
||||||
|
errors: HashMap<usize, Vec<Errors>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Evaluator<'a> {
|
||||||
|
pub fn init() -> Self {
|
||||||
|
Self {
|
||||||
|
curr_phase: EvalPhase::Lex,
|
||||||
|
files: SimpleFiles::new(),
|
||||||
|
errors: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, input: String, name: Option<&'a str>) {
|
||||||
|
let fid = self.files.add(name.unwrap_or("input"), input.clone());
|
||||||
|
|
||||||
|
let syntax = parse_syntax(&input);
|
||||||
|
|
||||||
|
match syntax {
|
||||||
|
Ok(syntax) => self.curr_phase = EvalPhase::Check(fid, syntax),
|
||||||
|
Err(errs) => {
|
||||||
|
self.errors.insert(
|
||||||
|
fid,
|
||||||
|
vec![Errors {
|
||||||
|
kind: ErrorKind::InvalidToken,
|
||||||
|
locs: errs,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
self.curr_phase = EvalPhase::Failed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(mut self) {
|
||||||
|
match self.curr_phase {
|
||||||
|
EvalPhase::Lex => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
EvalPhase::Check(file_id, syntax) => {
|
||||||
|
let r = check(&syntax);
|
||||||
|
|
||||||
|
if let Err(errs) = r {
|
||||||
|
self.errors.insert(file_id, errs);
|
||||||
|
self.curr_phase = EvalPhase::Failed;
|
||||||
|
} else {
|
||||||
|
self.curr_phase = EvalPhase::BareTyped(file_id, syntax.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EvalPhase::BareTyped(file_id, syntax) => {
|
||||||
|
let ns = initialise_globals();
|
||||||
|
let r = into_typed_repr(&ns, syntax);
|
||||||
|
|
||||||
|
if let Err(errs) = r {
|
||||||
|
self.errors.insert(file_id, vec![errs]);
|
||||||
|
self.curr_phase = EvalPhase::Failed;
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EvalPhase::Failed => self.error_out().unwrap(),
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_out(self) -> Result<!, codespan_reporting::files::Error> {
|
||||||
|
let Evaluator {
|
||||||
|
curr_phase,
|
||||||
|
files,
|
||||||
|
errors,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
|
let config = term::Config::default();
|
||||||
|
|
||||||
|
for (file_id, errors) in errors.iter() {
|
||||||
|
let writer = &mut writer.lock();
|
||||||
|
for error in errors {
|
||||||
|
term::emit(writer, &config, &files, &error.into_diag(*file_id, &files))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EvalPhase {
|
||||||
|
Lex,
|
||||||
|
Check(usize, Vec<PipelineElement>),
|
||||||
|
BareTyped(usize, Vec<PipelineElement>),
|
||||||
|
Failed,
|
||||||
|
}
|
62
src/main.rs
62
src/main.rs
|
@ -1,64 +1,28 @@
|
||||||
|
#![feature(never_type)]
|
||||||
|
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use args::Args;
|
use args::Args;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use codespan_reporting::{
|
use evaluator::Evaluator;
|
||||||
files::SimpleFiles,
|
|
||||||
term::{
|
|
||||||
self,
|
|
||||||
termcolor::{ColorChoice, StandardStream},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use syntax::{check::check, error::SyntaxError, parse_syntax};
|
|
||||||
|
|
||||||
use crate::{builtins::initialise_globals, typed::into_typed_repr};
|
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
mod builtins;
|
mod builtins;
|
||||||
|
mod error;
|
||||||
|
mod evaluator;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod namespace;
|
mod namespace;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
mod typed;
|
mod typed;
|
||||||
|
|
||||||
|
// basically logos::Span but in this repo
|
||||||
|
type Span = Range<usize>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let syntax = parse_syntax(&args.text);
|
let mut evaluator = Evaluator::init();
|
||||||
|
|
||||||
if args.debug_tokens {
|
evaluator.run(args.text, None);
|
||||||
println!("Tokens: {syntax:#?}");
|
evaluator.next();
|
||||||
}
|
|
||||||
|
|
||||||
let mut files = SimpleFiles::new();
|
|
||||||
|
|
||||||
let input_id = files.add("input", args.text);
|
|
||||||
|
|
||||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
|
||||||
let config = term::Config::default();
|
|
||||||
|
|
||||||
if let Err(errs) = syntax {
|
|
||||||
let writer = &mut writer.lock();
|
|
||||||
|
|
||||||
term::emit(
|
|
||||||
writer,
|
|
||||||
&config,
|
|
||||||
&files,
|
|
||||||
&SyntaxError::InvalidToken(errs).to_diagnostic(input_id),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
let check_res = check(&syntax.clone().unwrap());
|
|
||||||
|
|
||||||
if let Err(errs) = check_res {
|
|
||||||
let writer = &mut writer.lock();
|
|
||||||
|
|
||||||
let diags = errs.into_iter().map(|err| err.to_diagnostic(input_id));
|
|
||||||
|
|
||||||
for diag in diags {
|
|
||||||
term::emit(writer, &config, &files, &diag).unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let ns = initialise_globals();
|
|
||||||
|
|
||||||
println!("typed: {:#?}", into_typed_repr(&ns, syntax.unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,56 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
use crate::syntax::CommandPart;
|
use crate::{
|
||||||
|
error::{ErrorKind, Errors, SyntaxErrorKind},
|
||||||
|
syntax::CommandPart,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{error::SyntaxError, CommandPartKind, PipelineElement, PipelineElementKind};
|
use super::{CommandPartKind, PipelineElement, PipelineElementKind};
|
||||||
|
|
||||||
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<SyntaxError>> {
|
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<Errors>> {
|
||||||
let mut errs = Vec::new();
|
let mut errs = Vec::new();
|
||||||
|
|
||||||
if let Err(e_span) = check_missing_streamer(syntax) {
|
if let Err(e_span) = check_missing_streamer(syntax) {
|
||||||
errs.push(SyntaxError::MissingStreamer(vec![e_span]));
|
errs.push(Errors::new_single(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingStreamer),
|
||||||
|
e_span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err_locs) = check_missing_filters(syntax) {
|
if let Err(err_locs) = check_missing_filters(syntax) {
|
||||||
errs.push(SyntaxError::MissingFilter(err_locs));
|
errs.push(Errors::new(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingFilter),
|
||||||
|
err_locs,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e_span) = check_missing_sink(syntax) {
|
if let Err(e_span) = check_missing_sink(syntax) {
|
||||||
errs.push(SyntaxError::MissingSink(vec![e_span]));
|
errs.push(Errors::new_single(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingSink),
|
||||||
|
e_span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e_span) = check_literal_as_sink(syntax) {
|
if let Err(e_span) = check_literal_as_sink(syntax) {
|
||||||
errs.push(SyntaxError::LiteralAsSink(vec![e_span]));
|
errs.push(Errors::new_single(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsSink),
|
||||||
|
e_span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err_locs) = check_literal_as_filter(syntax) {
|
if let Err(err_locs) = check_literal_as_filter(syntax) {
|
||||||
errs.push(SyntaxError::LiteralAsFilter(err_locs));
|
errs.push(Errors::new(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsFilter),
|
||||||
|
err_locs,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e_span) = check_literal_with_args(syntax) {
|
if let Err(e_span) = check_literal_with_args(syntax) {
|
||||||
errs.push(SyntaxError::LiteralWithArgs(vec![e_span]));
|
errs.push(Errors::new_single(
|
||||||
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralWithArgs),
|
||||||
|
e_span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs.is_empty() {
|
if errs.is_empty() {
|
||||||
|
@ -127,6 +148,8 @@ fn check_literal_as_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span>
|
||||||
fn check_literal_as_filter(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
|
fn check_literal_as_filter(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
|
||||||
let errs = syntax
|
let errs = syntax
|
||||||
.iter()
|
.iter()
|
||||||
|
.take(syntax.len() - 1)
|
||||||
|
.skip(1)
|
||||||
.filter(|element| {
|
.filter(|element| {
|
||||||
!matches!(
|
!matches!(
|
||||||
element,
|
element,
|
||||||
|
@ -155,8 +178,6 @@ fn check_literal_as_filter(syntax: &[PipelineElement]) -> Result<(), Vec<logos::
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.skip(1)
|
|
||||||
.take(syntax.len() - 1)
|
|
||||||
.filter_map(|err| err.map(Clone::clone))
|
.filter_map(|err| err.map(Clone::clone))
|
||||||
.collect::<Vec<logos::Span>>();
|
.collect::<Vec<logos::Span>>();
|
||||||
|
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
|
||||||
|
|
||||||
/// The enum representing a syntax error, used for error reporting
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum SyntaxError {
|
|
||||||
/// This variant indicates a token that the Lexer didn't recognize
|
|
||||||
InvalidToken(Vec<logos::Span>),
|
|
||||||
/// `MissingStreamer` means, that the pipeline starts with a Pipe (`|`), so it has no streamer as input in front of it.
|
|
||||||
MissingStreamer(Vec<logos::Span>),
|
|
||||||
/// `MissingSink` means, that the pipeline ends with a Pipe (`|`), meaning that the output can't go anywhere
|
|
||||||
MissingSink(Vec<logos::Span>),
|
|
||||||
/// This indicates a missing filter somewhere in the pipeline, meaning that there's 2 pipes after one another
|
|
||||||
MissingFilter(Vec<logos::Span>),
|
|
||||||
/// A literal cannot be a sink, TODO
|
|
||||||
LiteralAsSink(Vec<logos::Span>),
|
|
||||||
/// A literal can't be a filter either, TODO
|
|
||||||
LiteralAsFilter(Vec<logos::Span>),
|
|
||||||
/// A literal acting as streamer cannot take arguments, TODO
|
|
||||||
LiteralWithArgs(Vec<logos::Span>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: much better and more complex errors, with suggestions for fixes
|
|
||||||
impl SyntaxError {
|
|
||||||
pub fn to_diagnostic(&self, file_id: usize) -> Diagnostic<usize> {
|
|
||||||
match self {
|
|
||||||
Self::InvalidToken(errs) => Diagnostic::error()
|
|
||||||
.with_message("failed to parse invalid tokens")
|
|
||||||
.with_labels(
|
|
||||||
errs.iter()
|
|
||||||
.map(|span| {
|
|
||||||
Label::primary(file_id, span.clone()).with_message("invalid token")
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::MissingStreamer(locs) => Diagnostic::error()
|
|
||||||
.with_message("pipelines must always start with a provider")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::MissingFilter(locs) => Diagnostic::error()
|
|
||||||
.with_message("missing filters in pipeline")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::MissingSink(locs) => Diagnostic::error()
|
|
||||||
.with_message("pipelines need to end in a sink")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::LiteralAsSink(locs) => Diagnostic::error()
|
|
||||||
.with_message("literals cannot be sinks")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::LiteralAsFilter(locs) => Diagnostic::error()
|
|
||||||
.with_message("literals cannot be filters")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Self::LiteralWithArgs(locs) => Diagnostic::error()
|
|
||||||
.with_message("literals cannot take arguments")
|
|
||||||
.with_labels(
|
|
||||||
locs.iter()
|
|
||||||
.map(|span| Label::primary(file_id, span.clone()))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ use logos::Span;
|
||||||
use crate::lexer::Token;
|
use crate::lexer::Token;
|
||||||
|
|
||||||
pub mod check;
|
pub mod check;
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PipelineElement {
|
pub struct PipelineElement {
|
||||||
|
|
32
src/typed.rs
32
src/typed.rs
|
@ -2,14 +2,16 @@ use core::panic;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builtins::{TYPE_FLOAT, TYPE_INTEGER, TYPE_STRING},
|
builtins::{TYPE_FLOAT, TYPE_INTEGER, TYPE_STRING},
|
||||||
|
error::Errors,
|
||||||
namespace::{command::Command, r#type::Type, GlobalNamespace},
|
namespace::{command::Command, r#type::Type, GlobalNamespace},
|
||||||
syntax::{CommandPart, CommandPartKind, PipelineElement, PipelineElementKind},
|
syntax::{CommandPart, CommandPartKind, PipelineElement, PipelineElementKind},
|
||||||
|
Span,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expr<'a> {
|
pub struct Expr<'a> {
|
||||||
kind: ExprKind<'a>,
|
kind: ExprKind<'a>,
|
||||||
span: logos::Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -38,8 +40,12 @@ impl LiteralKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_typed_repr(ns: &GlobalNamespace, syntax: Vec<PipelineElement>) -> Vec<Expr> {
|
pub fn into_typed_repr(
|
||||||
|
ns: &GlobalNamespace,
|
||||||
|
syntax: Vec<PipelineElement>,
|
||||||
|
) -> Result<Vec<Expr>, Errors> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
|
let mut errs = Vec::new();
|
||||||
|
|
||||||
for item in syntax {
|
for item in syntax {
|
||||||
let PipelineElement { kind, span } = item;
|
let PipelineElement { kind, span } = item;
|
||||||
|
@ -51,7 +57,13 @@ pub fn into_typed_repr(ns: &GlobalNamespace, syntax: Vec<PipelineElement>) -> Ve
|
||||||
res.push(Expr {
|
res.push(Expr {
|
||||||
kind: match kind {
|
kind: match kind {
|
||||||
CommandPartKind::Word(val) => ExprKind::Command {
|
CommandPartKind::Word(val) => ExprKind::Command {
|
||||||
command: ns.get_command_by_name(val).unwrap(),
|
command: {
|
||||||
|
let Some(c) = ns.get_command_by_name(val) else {
|
||||||
|
errs.push(span);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
c
|
||||||
|
},
|
||||||
args: Vec::new(),
|
args: Vec::new(),
|
||||||
},
|
},
|
||||||
CommandPartKind::Integer(val) => {
|
CommandPartKind::Integer(val) => {
|
||||||
|
@ -77,7 +89,13 @@ pub fn into_typed_repr(ns: &GlobalNamespace, syntax: Vec<PipelineElement>) -> Ve
|
||||||
|
|
||||||
res.push(Expr {
|
res.push(Expr {
|
||||||
kind: ExprKind::Command {
|
kind: ExprKind::Command {
|
||||||
command: ns.get_command_by_name(name).unwrap(),
|
command: {
|
||||||
|
let Some(c) = ns.get_command_by_name(name) else {
|
||||||
|
errs.push(span.clone());
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
c
|
||||||
|
},
|
||||||
args: c
|
args: c
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
@ -104,5 +122,9 @@ pub fn into_typed_repr(ns: &GlobalNamespace, syntax: Vec<PipelineElement>) -> Ve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
if errs.is_empty() {
|
||||||
|
Ok(res)
|
||||||
|
} else {
|
||||||
|
Err(Errors::new(crate::error::ErrorKind::CommandNotFound, errs))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue