Add nu-protocol

This commit is contained in:
JT
2021-09-02 13:29:43 +12:00
parent c4c4d82bf4
commit 3d252a9797
35 changed files with 296 additions and 247 deletions

View File

@ -1,7 +0,0 @@
use crate::{BlockId, Signature};
#[derive(Clone, Debug)]
pub struct Declaration {
pub signature: Box<Signature>,
pub body: Option<BlockId>,
}

View File

@ -1,9 +1,7 @@
use crate::parser_state::Type;
use crate::ParserWorkingSet;
use nu_protocol::{Span, Type};
use std::ops::Range;
pub use crate::Span;
#[derive(Debug)]
pub enum ParseError {
ExtraTokens(Span),

View File

@ -1,4 +1,5 @@
use crate::{Block, Expr, Expression, ParserWorkingSet, Pipeline, Span, Statement};
use crate::{Block, Expr, Expression, ParserWorkingSet, Pipeline, Statement};
use nu_protocol::Span;
#[derive(Debug)]
pub enum FlatShape {

View File

@ -1,4 +1,5 @@
use crate::{ParseError, Span};
use crate::ParseError;
use nu_protocol::Span;
#[derive(Debug, PartialEq, Eq)]
pub enum TokenContents {
@ -307,4 +308,4 @@ pub fn lex(
}
}
(output, error)
}
}

View File

@ -1,23 +1,14 @@
mod declaration;
mod errors;
mod flatten;
mod lex;
mod lite_parse;
mod parser;
mod parser_state;
mod signature;
mod span;
mod type_check;
pub use declaration::Declaration;
pub use errors::ParseError;
pub use flatten::FlatShape;
pub use lex::{lex, Token, TokenContents};
pub use lite_parse::{lite_parse, LiteBlock};
pub use parser::{
span, Block, Call, Expr, Expression, Import, Operator, Pipeline, Statement, SyntaxShape,
VarDecl,
};
pub use parser_state::{BlockId, DeclId, ParserDelta, ParserState, ParserWorkingSet, Type, VarId};
pub use signature::{Flag, PositionalArg, Signature};
pub use span::Span;
pub use parser::{Block, Call, Expr, Expression, Import, Operator, Pipeline, Statement, VarDecl};
pub use parser_state::{ParserDelta, ParserState, ParserWorkingSet};

View File

@ -1,4 +1,5 @@
use crate::{ParseError, Span, Token, TokenContents};
use crate::{ParseError, Token, TokenContents};
use nu_protocol::Span;
#[derive(Debug)]
pub struct LiteCommand {

View File

@ -3,117 +3,13 @@ use std::{
ops::{Index, IndexMut},
};
use crate::{
lex, lite_parse,
parser_state::{Type, VarId},
signature::{Flag, PositionalArg},
BlockId, DeclId, Declaration, LiteBlock, ParseError, ParserWorkingSet, Signature, Span, Token,
TokenContents,
use crate::{lex, lite_parse, LiteBlock, ParseError, ParserWorkingSet, Token, TokenContents};
use nu_protocol::{
span, BlockId, DeclId, Declaration, Flag, PositionalArg, Signature, Span, SyntaxShape, Type,
VarId,
};
/// The syntactic shapes that values must match to be passed into a command. You can think of this as the type-checking that occurs when you call a function.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxShape {
/// A specific match to a word or symbol
Keyword(Vec<u8>, Box<SyntaxShape>),
/// Any syntactic form is allowed
Any,
/// Strings and string-like bare words are allowed
String,
/// A dotted path to navigate the table
ColumnPath,
/// A dotted path to navigate the table (including variable)
FullColumnPath,
/// Only a numeric (integer or decimal) value is allowed
Number,
/// A range is allowed (eg, `1..3`)
Range,
/// Only an integer value is allowed
Int,
/// A filepath is allowed
FilePath,
/// A glob pattern is allowed, eg `foo*`
GlobPattern,
/// A block is allowed, eg `{start this thing}`
Block,
/// A table is allowed, eg `[[first, second]; [1, 2]]`
Table,
/// A table is allowed, eg `[first second]`
List(Box<SyntaxShape>),
/// A filesize value is allowed, eg `10kb`
Filesize,
/// A duration value is allowed, eg `19day`
Duration,
/// An operator
Operator,
/// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1`
/// The shorthand allows us to more easily reach columns inside of the row being passed in
RowCondition,
/// A general math expression, eg `1 + 2`
MathExpression,
/// A variable name
Variable,
/// A variable with optional type, `x` or `x: int`
VarWithOptType,
/// A signature for a definition, `[x:int, --foo]`
Signature,
/// A general expression, eg `1 + 2` or `foo --bar`
Expression,
}
impl SyntaxShape {
pub fn to_type(&self) -> Type {
match self {
SyntaxShape::Any => Type::Unknown,
SyntaxShape::Block => Type::Block,
SyntaxShape::ColumnPath => Type::Unknown,
SyntaxShape::Duration => Type::Duration,
SyntaxShape::Expression => Type::Unknown,
SyntaxShape::FilePath => Type::FilePath,
SyntaxShape::Filesize => Type::Filesize,
SyntaxShape::FullColumnPath => Type::Unknown,
SyntaxShape::GlobPattern => Type::String,
SyntaxShape::Int => Type::Int,
SyntaxShape::List(x) => {
let contents = x.to_type();
Type::List(Box::new(contents))
}
SyntaxShape::Keyword(_, expr) => expr.to_type(),
SyntaxShape::MathExpression => Type::Unknown,
SyntaxShape::Number => Type::Number,
SyntaxShape::Operator => Type::Unknown,
SyntaxShape::Range => Type::Unknown,
SyntaxShape::RowCondition => Type::Bool,
SyntaxShape::Signature => Type::Unknown,
SyntaxShape::String => Type::String,
SyntaxShape::Table => Type::Table,
SyntaxShape::VarWithOptType => Type::Unknown,
SyntaxShape::Variable => Type::Unknown,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
Equal,
@ -404,21 +300,6 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseError>
}
}
pub fn span(spans: &[Span]) -> Span {
let length = spans.len();
if length == 0 {
Span::unknown()
} else if length == 1 {
spans[0]
} else {
Span {
start: spans[0].start,
end: spans[length - 1].end,
}
}
}
impl<'a> ParserWorkingSet<'a> {
pub fn parse_external_call(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) {
// TODO: add external parsing
@ -725,7 +606,7 @@ impl<'a> ParserWorkingSet<'a> {
call.decl_id = decl_id;
call.head = command_span;
let decl = self.get_decl(decl_id).clone();
let signature = self.get_decl(decl_id).signature.clone();
// The index into the positional parameter in the definition
let mut positional_idx = 0;
@ -738,8 +619,7 @@ impl<'a> ParserWorkingSet<'a> {
let arg_span = spans[spans_idx];
// Check if we're on a long flag, if so, parse
let (long_name, arg, err) =
self.parse_long_flag(spans, &mut spans_idx, &decl.signature);
let (long_name, arg, err) = self.parse_long_flag(spans, &mut spans_idx, &signature);
if let Some(long_name) = long_name {
// We found a long flag, like --bar
error = error.or(err);
@ -750,7 +630,7 @@ impl<'a> ParserWorkingSet<'a> {
// Check if we're on a short flag or group of short flags, if so, parse
let (short_flags, err) =
self.parse_short_flags(spans, &mut spans_idx, positional_idx, &decl.signature);
self.parse_short_flags(spans, &mut spans_idx, positional_idx, &signature);
if let Some(short_flags) = short_flags {
error = error.or(err);
@ -774,8 +654,9 @@ impl<'a> ParserWorkingSet<'a> {
}
// Parse a positional arg if there is one
if let Some(positional) = decl.signature.get_positional(positional_idx) {
if let Some(positional) = signature.get_positional(positional_idx) {
//Make sure we leave enough spans for the remaining positionals
let decl = self.get_decl(decl_id);
let end = self.calculate_end_span(&decl, spans, spans_idx, positional_idx);
@ -813,7 +694,7 @@ impl<'a> ParserWorkingSet<'a> {
spans_idx += 1;
}
let err = check_call(command_span, &decl.signature, &call);
let err = check_call(command_span, &signature, &call);
error = error.or(err);
// FIXME: type unknown

View File

@ -1,8 +1,8 @@
use crate::{parser::Block, Declaration, Span};
use crate::parser::Block;
use core::panic;
use std::{collections::HashMap, fmt::Display, slice::Iter};
use nu_protocol::{BlockId, DeclId, Declaration, Span, Type, VarId};
use std::{collections::HashMap, slice::Iter};
#[derive(Debug)]
pub struct ParserState {
files: Vec<(String, usize, usize)>,
file_contents: Vec<u8>,
@ -12,49 +12,6 @@ pub struct ParserState {
scope: Vec<ScopeFrame>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
Int,
Float,
Bool,
String,
Block,
ColumnPath,
Duration,
FilePath,
Filesize,
List(Box<Type>),
Number,
Nothing,
Table,
Unknown,
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Block => write!(f, "block"),
Type::Bool => write!(f, "bool"),
Type::ColumnPath => write!(f, "column path"),
Type::Duration => write!(f, "duration"),
Type::FilePath => write!(f, "filepath"),
Type::Filesize => write!(f, "filesize"),
Type::Float => write!(f, "float"),
Type::Int => write!(f, "int"),
Type::List(l) => write!(f, "list<{}>", l),
Type::Nothing => write!(f, "nothing"),
Type::Number => write!(f, "number"),
Type::String => write!(f, "string"),
Type::Table => write!(f, "table"),
Type::Unknown => write!(f, "unknown"),
}
}
}
pub type VarId = usize;
pub type DeclId = usize;
pub type BlockId = usize;
#[derive(Debug)]
struct ScopeFrame {
vars: HashMap<Vec<u8>, VarId>,
@ -136,7 +93,7 @@ impl ParserState {
pub fn print_decls(&self) {
for decl in self.decls.iter().enumerate() {
println!("decl{}: {:?}", decl.0, decl.1);
println!("decl{}: {:?}", decl.0, decl.1.signature);
}
}
@ -219,13 +176,11 @@ impl ParserState {
}
}
#[derive(Debug)]
pub struct ParserWorkingSet<'a> {
permanent_state: &'a ParserState,
pub delta: ParserDelta,
}
#[derive(Debug)]
pub struct ParserDelta {
files: Vec<(String, usize, usize)>,
pub(crate) file_contents: Vec<u8>,

View File

@ -1,293 +0,0 @@
use crate::{parser::SyntaxShape, Declaration, VarId};
#[derive(Debug, Clone)]
pub struct Flag {
pub long: String,
pub short: Option<char>,
pub arg: Option<SyntaxShape>,
pub required: bool,
pub desc: String,
// For custom commands
pub var_id: Option<VarId>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PositionalArg {
pub name: String,
pub desc: String,
pub shape: SyntaxShape,
// For custom commands
pub var_id: Option<VarId>,
}
#[derive(Clone, Debug)]
pub struct Signature {
pub name: String,
pub usage: String,
pub extra_usage: String,
pub required_positional: Vec<PositionalArg>,
pub optional_positional: Vec<PositionalArg>,
pub rest_positional: Option<PositionalArg>,
pub named: Vec<Flag>,
pub is_filter: bool,
}
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.usage == other.usage
&& self.required_positional == other.required_positional
&& self.optional_positional == other.optional_positional
&& self.rest_positional == other.rest_positional
&& self.is_filter == other.is_filter
}
}
impl Eq for Signature {}
impl Signature {
pub fn new(name: impl Into<String>) -> Signature {
Signature {
name: name.into(),
usage: String::new(),
extra_usage: String::new(),
required_positional: vec![],
optional_positional: vec![],
rest_positional: None,
named: vec![],
is_filter: false,
}
}
pub fn build(name: impl Into<String>) -> Signature {
Signature::new(name.into())
}
/// Add a description to the signature
pub fn desc(mut self, usage: impl Into<String>) -> Signature {
self.usage = usage.into();
self
}
/// Add a required positional argument to the signature
pub fn required(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> Signature {
self.required_positional.push(PositionalArg {
name: name.into(),
desc: desc.into(),
shape: shape.into(),
var_id: None,
});
self
}
/// Add a required positional argument to the signature
pub fn optional(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> Signature {
self.optional_positional.push(PositionalArg {
name: name.into(),
desc: desc.into(),
shape: shape.into(),
var_id: None,
});
self
}
pub fn rest(mut self, shape: impl Into<SyntaxShape>, desc: impl Into<String>) -> Signature {
self.rest_positional = Some(PositionalArg {
name: "rest".into(),
desc: desc.into(),
shape: shape.into(),
var_id: None,
});
self
}
/// Add an optional named flag argument to the signature
pub fn named(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
let s = short.map(|c| {
debug_assert!(!self.get_shorts().contains(&c));
c
});
self.named.push(Flag {
long: name.into(),
short: s,
arg: Some(shape.into()),
required: false,
desc: desc.into(),
var_id: None,
});
self
}
/// Add a required named flag argument to the signature
pub fn required_named(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
let s = short.map(|c| {
debug_assert!(!self.get_shorts().contains(&c));
c
});
self.named.push(Flag {
long: name.into(),
short: s,
arg: Some(shape.into()),
required: true,
desc: desc.into(),
var_id: None,
});
self
}
/// Add a switch to the signature
pub fn switch(
mut self,
name: impl Into<String>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
let s = short.map(|c| {
debug_assert!(
!self.get_shorts().contains(&c),
"There may be duplicate short flags, such as -h"
);
c
});
self.named.push(Flag {
long: name.into(),
short: s,
arg: None,
required: false,
desc: desc.into(),
var_id: None,
});
self
}
/// Get list of the short-hand flags
pub fn get_shorts(&self) -> Vec<char> {
let mut shorts = Vec::new();
for Flag { short, .. } in &self.named {
if let Some(c) = short {
shorts.push(*c);
}
}
shorts
}
pub fn get_positional(&self, position: usize) -> Option<PositionalArg> {
if position < self.required_positional.len() {
self.required_positional.get(position).cloned()
} else if position < (self.required_positional.len() + self.optional_positional.len()) {
self.optional_positional
.get(position - self.required_positional.len())
.cloned()
} else {
self.rest_positional.clone()
}
}
pub fn num_positionals(&self) -> usize {
let mut total = self.required_positional.len() + self.optional_positional.len();
for positional in &self.required_positional {
if let SyntaxShape::Keyword(..) = positional.shape {
// Keywords have a required argument, so account for that
total += 1;
}
}
for positional in &self.optional_positional {
if let SyntaxShape::Keyword(..) = positional.shape {
// Keywords have a required argument, so account for that
total += 1;
}
}
total
}
pub fn num_positionals_after(&self, idx: usize) -> usize {
let mut total = 0;
let mut curr = 0;
for positional in &self.required_positional {
match positional.shape {
SyntaxShape::Keyword(..) => {
// Keywords have a required argument, so account for that
if curr > idx {
total += 2;
}
}
_ => {
if curr > idx {
total += 1;
}
}
}
curr += 1;
}
total
}
/// Find the matching long flag
pub fn get_long_flag(&self, name: &str) -> Option<Flag> {
for flag in &self.named {
if flag.long == name {
return Some(flag.clone());
}
}
None
}
/// Find the matching long flag
pub fn get_short_flag(&self, short: char) -> Option<Flag> {
for flag in &self.named {
if let Some(short_flag) = &flag.short {
if *short_flag == short {
return Some(flag.clone());
}
}
}
None
}
}
impl From<Box<Signature>> for Declaration {
fn from(val: Box<Signature>) -> Self {
Declaration {
signature: val,
body: None,
}
}
}
impl From<Signature> for Declaration {
fn from(val: Signature) -> Self {
Declaration {
signature: Box::new(val),
body: None,
}
}
}

View File

@ -1,22 +0,0 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Span {
Span { start, end }
}
pub fn unknown() -> Span {
Span { start: 0, end: 0 }
}
pub fn offset(&self, offset: usize) -> Span {
Span {
start: self.start - offset,
end: self.end - offset,
}
}
}

View File

@ -1,4 +1,5 @@
use crate::{parser::Operator, parser_state::Type, Expr, Expression, ParseError, ParserWorkingSet};
use crate::{parser::Operator, Expr, Expression, ParseError, ParserWorkingSet};
use nu_protocol::Type;
impl<'a> ParserWorkingSet<'a> {
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {