mirror of
https://github.com/nushell/nushell.git
synced 2025-08-10 03:58:03 +02:00
A real parser (lalrpop)
This commit is contained in:
@ -1,144 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{escaped, is_a, is_not, tag};
|
||||
use nom::character::complete::one_of;
|
||||
use nom::multi::separated_list;
|
||||
use nom::sequence::{preceded, terminated};
|
||||
use nom::IResult;
|
||||
use nom::{complete, named, ws};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Item {
|
||||
Quoted(String),
|
||||
Bare(String),
|
||||
Int(i64),
|
||||
Boolean(bool),
|
||||
Operator(Operator),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Operator {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn print(&self) -> String {
|
||||
match *self {
|
||||
Operator::Equal => "==".to_string(),
|
||||
Operator::NotEqual => "!=".to_string(),
|
||||
Operator::LessThan => "<".to_string(),
|
||||
Operator::GreaterThan => ">".to_string(),
|
||||
Operator::LessThanOrEqual => "<=".to_string(),
|
||||
Operator::GreaterThanOrEqual => ">=".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operator {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
match input {
|
||||
"==" => Ok(Operator::Equal),
|
||||
"!=" => Ok(Operator::NotEqual),
|
||||
"<" => Ok(Operator::LessThan),
|
||||
">" => Ok(Operator::GreaterThan),
|
||||
"<=" => Ok(Operator::LessThanOrEqual),
|
||||
">=" => Ok(Operator::GreaterThanOrEqual),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
crate fn as_value(&self) -> Value {
|
||||
match self {
|
||||
Item::Quoted(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||
Item::Bare(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||
Item::Int(i) => Value::Primitive(Primitive::Int(*i)),
|
||||
Item::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)),
|
||||
Item::Operator(o) => Value::Primitive(Primitive::Operator(o.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) -> String {
|
||||
match self {
|
||||
Item::Bare(s) => format!("{}", s),
|
||||
Item::Quoted(s) => format!("{}", s),
|
||||
Item::Int(i) => format!("{:?}", i),
|
||||
Item::Boolean(b) => format!("{:?}", b),
|
||||
Item::Operator(o) => o.print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
crate fn name(&self) -> Result<&str, ShellError> {
|
||||
match self {
|
||||
Item::Quoted(s) => Ok(s),
|
||||
Item::Bare(s) => Ok(s),
|
||||
Item::Boolean(i) => Err(ShellError::string(format!("{} is not a valid command", i))),
|
||||
Item::Int(i) => Err(ShellError::string(format!("{} is not a valid command", i))),
|
||||
Item::Operator(x) => Err(ShellError::string(format!(
|
||||
"{:?} is not a valid command",
|
||||
x
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn esc(s: &str) -> IResult<&str, &str> {
|
||||
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
|
||||
}
|
||||
|
||||
fn quoted(s: &str) -> IResult<&str, Item> {
|
||||
terminated(preceded(tag("\""), esc), tag("\""))(s)
|
||||
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
|
||||
}
|
||||
|
||||
fn unquoted(s: &str) -> IResult<&str, Item> {
|
||||
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
|
||||
}
|
||||
|
||||
fn operator(s: &str) -> IResult<&str, Item> {
|
||||
alt((
|
||||
tag("=="),
|
||||
tag("!="),
|
||||
tag("<"),
|
||||
tag(">"),
|
||||
tag("<="),
|
||||
tag(">="),
|
||||
))(s)
|
||||
.map(|(a, b)| (a, Item::Operator(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn int(s: &str) -> IResult<&str, Item> {
|
||||
is_a("1234567890")(s).map(|(a, b)| (a, Item::Int(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn boolean(s: &str) -> IResult<&str, Item> {
|
||||
alt((tag("true"), tag("false")))(s)
|
||||
.map(|(a, b)| (a, Item::Boolean(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn command_token(s: &str) -> IResult<&str, Item> {
|
||||
alt((boolean, int, operator, quoted, unquoted))(s)
|
||||
}
|
||||
|
||||
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
|
||||
separated_list(tag(" "), command_token)(s)
|
||||
}
|
||||
|
||||
named!(
|
||||
pub shell_parser(&str) -> Vec<Vec<Item>>,
|
||||
complete!(
|
||||
ws!(
|
||||
separated_list!(tag("|"), command_args)
|
||||
)
|
||||
)
|
||||
);
|
56
src/parser/parser.lalrpop
Normal file
56
src/parser/parser.lalrpop
Normal file
@ -0,0 +1,56 @@
|
||||
use std::str::FromStr;
|
||||
use crate::parser::tokens::*;
|
||||
|
||||
grammar;
|
||||
|
||||
pub Pipeline: Pipeline = {
|
||||
<first:Command> => Pipeline::new(vec![first]),
|
||||
<first:Command> <rest: ( "|" <Command> )+> => Pipeline::from_parts(first, rest),
|
||||
}
|
||||
|
||||
Command: ParsedCommand = {
|
||||
<command:RawBareWord> <expr:Expr*> => ParsedCommand::new(command, expr)
|
||||
}
|
||||
|
||||
Expr: Expression = {
|
||||
<Leaf> => Expression::Leaf(<>),
|
||||
<Binary> => Expression::Binary(<>),
|
||||
}
|
||||
|
||||
Operator: Operator = {
|
||||
"==" => Operator::Equal,
|
||||
"!=" => Operator::NotEqual,
|
||||
"<" => Operator::LessThan,
|
||||
">" => Operator::GreaterThan,
|
||||
"<=" => Operator::LessThanOrEqual,
|
||||
">=" => Operator::GreaterThanOrEqual
|
||||
}
|
||||
|
||||
Binary: Binary = {
|
||||
<left:Leaf> <op:Operator> <right:Leaf> => Binary::new(left, op, right),
|
||||
}
|
||||
|
||||
Flag: Flag = {
|
||||
"-" <RawBareWord> => Flag::Shorthand(<>.to_string()),
|
||||
"--" <RawBareWord> => Flag::Longhand(<>.to_string()),
|
||||
}
|
||||
|
||||
String: String = {
|
||||
SQString,
|
||||
DQString,
|
||||
}
|
||||
|
||||
Leaf: Leaf = {
|
||||
<String> => Leaf::String(<>),
|
||||
<Num> => Leaf::Int(<>),
|
||||
<RawBareWord> => match <>.as_ref() {
|
||||
"true" => Leaf::Boolean(true),
|
||||
"false" => Leaf::Boolean(false),
|
||||
_ => Leaf::Bare(<>),
|
||||
}
|
||||
}
|
||||
|
||||
RawBareWord: String = <s:r#"[^0-9"'\-][^\s]*"#> => <>.to_string();
|
||||
DQString: String = <s:r#""([^"]|\\")*""#> => s[1..s.len() - 1].to_string();
|
||||
SQString: String = <s:r#"'([^']|\\')*'"#> => s[1..s.len() - 1].to_string();
|
||||
Num: i64 = <s:r"-?[0-9]+"> => i64::from_str(s).unwrap();
|
2109
src/parser/parser.rs
Normal file
2109
src/parser/parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
21
src/parser/registry.rs
Normal file
21
src/parser/registry.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use indexmap::IndexMap;
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum CommandType {
|
||||
Switch,
|
||||
Single,
|
||||
Array,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct CommandConfig {
|
||||
crate name: String,
|
||||
crate mandatory_positional: Vec<String>,
|
||||
crate optional_positional: Vec<String>,
|
||||
crate rest_positional: bool,
|
||||
crate named: IndexMap<String, CommandType>,
|
||||
}
|
||||
|
||||
pub trait CommandRegistry {
|
||||
fn get(&self, name: &str) -> CommandConfig;
|
||||
}
|
128
src/parser/tokens.rs
Normal file
128
src/parser/tokens.rs
Normal file
@ -0,0 +1,128 @@
|
||||
use derive_new::new;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Operator {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn print(&self) -> String {
|
||||
match *self {
|
||||
Operator::Equal => "==".to_string(),
|
||||
Operator::NotEqual => "!=".to_string(),
|
||||
Operator::LessThan => "<".to_string(),
|
||||
Operator::GreaterThan => ">".to_string(),
|
||||
Operator::LessThanOrEqual => "<=".to_string(),
|
||||
Operator::GreaterThanOrEqual => ">=".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operator {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
match input {
|
||||
"==" => Ok(Operator::Equal),
|
||||
"!=" => Ok(Operator::NotEqual),
|
||||
"<" => Ok(Operator::LessThan),
|
||||
">" => Ok(Operator::GreaterThan),
|
||||
"<=" => Ok(Operator::LessThanOrEqual),
|
||||
">=" => Ok(Operator::GreaterThanOrEqual),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
Leaf(Leaf),
|
||||
Binary(Binary),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
crate fn print(&self) -> String {
|
||||
match self {
|
||||
Expression::Leaf(l) => l.print(),
|
||||
Expression::Binary(b) => b.print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Leaf {
|
||||
String(String),
|
||||
Bare(String),
|
||||
Boolean(bool),
|
||||
Int(i64),
|
||||
}
|
||||
|
||||
impl Leaf {
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Leaf::String(s) => format!("{:?}", s),
|
||||
Leaf::Bare(s) => format!("{}", s),
|
||||
Leaf::Boolean(b) => format!("{}", b),
|
||||
Leaf::Int(i) => format!("{}", i),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, new)]
|
||||
pub struct Binary {
|
||||
crate left: Leaf,
|
||||
crate operator: Operator,
|
||||
crate right: Leaf,
|
||||
}
|
||||
|
||||
impl Binary {
|
||||
fn print(&self) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
self.left.print(),
|
||||
self.operator.print(),
|
||||
self.right.print()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Flag {
|
||||
Shorthand(String),
|
||||
Longhand(String),
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
#[allow(unused)]
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Flag::Shorthand(s) => format!("-{}", s),
|
||||
Flag::Longhand(s) => format!("--{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new, Debug, Clone)]
|
||||
pub struct ParsedCommand {
|
||||
crate name: String,
|
||||
crate args: Vec<Expression>,
|
||||
}
|
||||
|
||||
#[derive(new, Debug)]
|
||||
pub struct Pipeline {
|
||||
crate commands: Vec<ParsedCommand>,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
crate fn from_parts(command: ParsedCommand, rest: Vec<ParsedCommand>) -> Pipeline {
|
||||
let mut commands = vec![command];
|
||||
commands.extend(rest);
|
||||
|
||||
Pipeline { commands }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user