A real parser (lalrpop)

This commit is contained in:
Yehuda Katz
2019-05-25 23:54:41 -07:00
parent cd92f579c9
commit b74daa2e60
24 changed files with 2599 additions and 216 deletions

View File

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

File diff suppressed because it is too large Load Diff

21
src/parser/registry.rs Normal file
View 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
View 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 }
}
}