drive let from internal call

This commit is contained in:
JT 2021-07-08 18:19:38 +12:00
parent 04cbef3aa8
commit 5d4ae4a2a4
3 changed files with 270 additions and 157 deletions

View file

@ -22,6 +22,16 @@ fn main() -> std::io::Result<()> {
.required("else_block", SyntaxShape::Block, "else block"); .required("else_block", SyntaxShape::Block, "else block");
working_set.add_decl((b"if").to_vec(), sig); working_set.add_decl((b"if").to_vec(), sig);
let sig = Signature::build("let")
.required("var_name", SyntaxShape::Variable, "variable name")
.required("=", SyntaxShape::Literal(b"=".to_vec()), "equals sign")
.required(
"value",
SyntaxShape::Expression,
"the value to set the variable to",
);
working_set.add_decl((b"let").to_vec(), sig);
//let file = std::fs::read(&path)?; //let file = std::fs::read(&path)?;
//let (output, err) = working_set.parse_file(&path, file); //let (output, err) = working_set.parse_file(&path, file);
let (output, err) = working_set.parse_source(path.as_bytes()); let (output, err) = working_set.parse_source(path.as_bytes());

View file

@ -17,4 +17,5 @@ pub enum ParseError {
MissingPositional(String, Span), MissingPositional(String, Span),
MissingRequiredFlag(String, Span), MissingRequiredFlag(String, Span),
IncompleteMathExpression(Span), IncompleteMathExpression(Span),
UnknownState(String, Span),
} }

View file

@ -3,7 +3,7 @@ use std::ops::{Index, IndexMut};
use crate::{ use crate::{
lex, lite_parse, lex, lite_parse,
parser_state::{Type, VarId}, parser_state::{Type, VarId},
span, DeclId, LiteBlock, ParseError, ParserWorkingSet, Signature, Span, DeclId, LiteBlock, ParseError, ParserWorkingSet, Signature, Span,
}; };
/// 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. /// 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.
@ -61,6 +61,9 @@ pub enum SyntaxShape {
/// A general math expression, eg `1 + 2` /// A general math expression, eg `1 + 2`
MathExpression, MathExpression,
/// A variable name
Variable,
/// A general expression, eg `1 + 2` or `foo --bar` /// A general expression, eg `1 + 2` or `foo --bar`
Expression, Expression,
} }
@ -300,189 +303,216 @@ impl ParserWorkingSet {
(Expression::garbage(spans[0]), None) (Expression::garbage(spans[0]), None)
} }
pub fn parse_call(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) { pub fn parse_internal_call(
&mut self,
spans: &[Span],
decl_id: usize,
) -> (Box<Call>, Span, Option<ParseError>) {
let mut error = None; let mut error = None;
// assume spans.len() > 0? let mut call = Call::new();
let name = self.get_span_contents(spans[0]); call.decl_id = decl_id;
if let Some(decl_id) = self.find_decl(name) { let sig = self
let mut call = Call::new(); .get_decl(decl_id)
call.decl_id = decl_id; .expect("internal error: bad DeclId")
.clone();
let sig = self let mut positional_idx = 0;
.get_decl(decl_id) let mut arg_offset = 1;
.expect("internal error: bad DeclId")
.clone();
let mut positional_idx = 0; while arg_offset < spans.len() {
let mut arg_offset = 1; let arg_span = spans[arg_offset];
let arg_contents = self.get_span_contents(arg_span);
if arg_contents.starts_with(&[b'-', b'-']) {
// FIXME: only use the first you find
let split: Vec<_> = arg_contents.split(|x| *x == b'=').collect();
let long_name = String::from_utf8(split[0].into());
if let Ok(long_name) = long_name {
if let Some(flag) = sig.get_long_flag(&long_name) {
if let Some(arg_shape) = &flag.arg {
if split.len() > 1 {
// and we also have the argument
let mut span = arg_span;
span.start += long_name.len() + 1; //offset by long flag and '='
let (arg, err) = self.parse_arg(span, arg_shape.clone());
error = error.or(err);
while arg_offset < spans.len() { call.named.push((long_name, Some(arg)));
let arg_span = spans[arg_offset]; } else if let Some(arg) = spans.get(arg_offset + 1) {
let arg_contents = self.get_span_contents(arg_span);
if arg_contents.starts_with(&[b'-', b'-']) {
// FIXME: only use the first you find
let split: Vec<_> = arg_contents.split(|x| *x == b'=').collect();
let long_name = String::from_utf8(split[0].into());
if let Ok(long_name) = long_name {
if let Some(flag) = sig.get_long_flag(&long_name) {
if let Some(arg_shape) = &flag.arg {
if split.len() > 1 {
// and we also have the argument
let mut span = arg_span;
span.start += long_name.len() + 1; //offset by long flag and '='
let (arg, err) = self.parse_arg(span, arg_shape.clone());
error = error.or(err);
call.named.push((long_name, Some(arg)));
} else if let Some(arg) = spans.get(arg_offset + 1) {
let (arg, err) = self.parse_arg(*arg, arg_shape.clone());
error = error.or(err);
call.named.push((long_name, Some(arg)));
arg_offset += 1;
} else {
error = error.or(Some(ParseError::MissingFlagParam(arg_span)))
}
}
} else {
error = error.or(Some(ParseError::UnknownFlag(arg_span)))
}
} else {
error = error.or(Some(ParseError::NonUtf8(arg_span)))
}
} else if arg_contents.starts_with(&[b'-']) && arg_contents.len() > 1 {
let short_flags = &arg_contents[1..];
let mut found_short_flags = vec![];
let mut unmatched_short_flags = vec![];
for short_flag in short_flags.iter().enumerate() {
let short_flag_char = char::from(*short_flag.1);
let orig = arg_span;
let short_flag_span = Span {
start: orig.start + 1 + short_flag.0,
end: orig.start + 1 + short_flag.0 + 1,
};
if let Some(flag) = sig.get_short_flag(short_flag_char) {
// If we require an arg and are in a batch of short flags, error
if !found_short_flags.is_empty() && flag.arg.is_some() {
error = error.or(Some(ParseError::ShortFlagBatchCantTakeArg(
short_flag_span,
)))
}
found_short_flags.push(flag);
} else {
unmatched_short_flags.push(short_flag_span);
}
}
if found_short_flags.is_empty() {
// check to see if we have a negative number
if let Some(positional) = sig.get_positional(positional_idx) {
if positional.shape == SyntaxShape::Int
|| positional.shape == SyntaxShape::Number
{
let (arg, err) = self.parse_arg(arg_span, positional.shape);
if err.is_some() {
if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else {
// We have successfully found a positional argument, move on
call.positional.push(arg);
positional_idx += 1;
}
} else if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else if !unmatched_short_flags.is_empty() {
if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
}
for flag in found_short_flags {
if let Some(arg_shape) = flag.arg {
if let Some(arg) = spans.get(arg_offset + 1) {
let (arg, err) = self.parse_arg(*arg, arg_shape.clone()); let (arg, err) = self.parse_arg(*arg, arg_shape.clone());
error = error.or(err); error = error.or(err);
call.named.push((flag.long.clone(), Some(arg))); call.named.push((long_name, Some(arg)));
arg_offset += 1; arg_offset += 1;
} else { } else {
error = error.or(Some(ParseError::MissingFlagParam(arg_span))) error = error.or(Some(ParseError::MissingFlagParam(arg_span)))
} }
} else {
call.named.push((flag.long.clone(), None));
} }
} else {
error = error.or(Some(ParseError::UnknownFlag(arg_span)))
} }
} else if let Some(positional) = sig.get_positional(positional_idx) { } else {
match positional.shape { error = error.or(Some(ParseError::NonUtf8(arg_span)))
SyntaxShape::RowCondition => { }
let remainder = sig.num_positionals() - positional_idx; } else if arg_contents.starts_with(&[b'-']) && arg_contents.len() > 1 {
let short_flags = &arg_contents[1..];
if spans.len() < remainder { let mut found_short_flags = vec![];
error = error.or_else(|| { let mut unmatched_short_flags = vec![];
Some(ParseError::MissingPositional( for short_flag in short_flags.iter().enumerate() {
"required args".into(), let short_flag_char = char::from(*short_flag.1);
arg_span, let orig = arg_span;
)) let short_flag_span = Span {
}); start: orig.start + 1 + short_flag.0,
} else { end: orig.start + 1 + short_flag.0 + 1,
let (arg, err) = self.parse_row_condition( };
&spans[arg_offset..(spans.len() - remainder + 1)], if let Some(flag) = sig.get_short_flag(short_flag_char) {
); // If we require an arg and are in a batch of short flags, error
error = error.or(err); if !found_short_flags.is_empty() && flag.arg.is_some() {
call.positional.push(arg); error = error
.or(Some(ParseError::ShortFlagBatchCantTakeArg(short_flag_span)))
arg_offset = spans.len() - remainder;
}
} }
SyntaxShape::Literal(literal) => { found_short_flags.push(flag);
if arg_contents != literal { } else {
// When keywords mismatch, this is a strong indicator of something going wrong. unmatched_short_flags.push(short_flag_span);
// We won't often override the current error, but as this is a strong indicator }
// go ahead and override the current error and tell the user about the missing }
// keyword/literal.
error = Some(ParseError::Mismatch( if found_short_flags.is_empty() {
format!("{}", String::from_utf8_lossy(&literal)), // check to see if we have a negative number
arg_span, if let Some(positional) = sig.get_positional(positional_idx) {
)) if positional.shape == SyntaxShape::Int
} || positional.shape == SyntaxShape::Number
call.positional.push(Expression { {
expr: Expr::Literal(literal),
span: arg_span,
});
}
_ => {
let (arg, err) = self.parse_arg(arg_span, positional.shape); let (arg, err) = self.parse_arg(arg_span, positional.shape);
if err.is_some() {
if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else {
// We have successfully found a positional argument, move on
call.positional.push(arg);
positional_idx += 1;
}
} else if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
} else if !unmatched_short_flags.is_empty() {
if let Some(first) = unmatched_short_flags.first() {
error = error.or(Some(ParseError::UnknownFlag(*first)));
}
}
for flag in found_short_flags {
if let Some(arg_shape) = flag.arg {
if let Some(arg) = spans.get(arg_offset + 1) {
let (arg, err) = self.parse_arg(*arg, arg_shape.clone());
error = error.or(err); error = error.or(err);
call.named.push((flag.long.clone(), Some(arg)));
arg_offset += 1;
} else {
error = error.or(Some(ParseError::MissingFlagParam(arg_span)))
}
} else {
call.named.push((flag.long.clone(), None));
}
}
} else if let Some(positional) = sig.get_positional(positional_idx) {
match positional.shape {
SyntaxShape::RowCondition => {
let remainder = sig.num_positionals() - positional_idx;
if spans.len() < remainder {
error = error.or_else(|| {
Some(ParseError::MissingPositional(
"required args".into(),
arg_span,
))
});
} else {
let (arg, err) = self.parse_row_condition(
&spans[arg_offset..(spans.len() - remainder + 1)],
);
error = error.or(err);
call.positional.push(arg); call.positional.push(arg);
arg_offset = spans.len() - remainder;
} }
} }
positional_idx += 1; SyntaxShape::Expression => {
} else { let remainder = sig.num_positionals() - positional_idx;
error = error.or(Some(ParseError::ExtraPositional(arg_span)))
if spans.len() < remainder {
error = error.or_else(|| {
Some(ParseError::MissingPositional(
"required args".into(),
arg_span,
))
});
} else {
let (arg, err) = self.parse_expression(
&spans[arg_offset..(spans.len() - remainder + 1)],
);
error = error.or(err);
call.positional.push(arg);
arg_offset = spans.len() - remainder;
}
}
SyntaxShape::Literal(literal) => {
if arg_contents != literal {
// When keywords mismatch, this is a strong indicator of something going wrong.
// We won't often override the current error, but as this is a strong indicator
// go ahead and override the current error and tell the user about the missing
// keyword/literal.
error = Some(ParseError::Mismatch(
String::from_utf8_lossy(&literal).into(),
arg_span,
))
}
call.positional.push(Expression {
expr: Expr::Literal(literal),
span: arg_span,
});
}
_ => {
let (arg, err) = self.parse_arg(arg_span, positional.shape);
error = error.or(err);
call.positional.push(arg);
}
} }
arg_offset += 1; positional_idx += 1;
} else {
error = error.or(Some(ParseError::ExtraPositional(arg_span)))
} }
arg_offset += 1;
}
let err = check_call(spans[0], &sig, &call); let err = check_call(spans[0], &sig, &call);
error = error.or(err); error = error.or(err);
// FIXME: type unknown // FIXME: type unknown
(Box::new(call), span(spans), error)
}
pub fn parse_call(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) {
// assume spans.len() > 0?
let name = self.get_span_contents(spans[0]);
if let Some(decl_id) = self.find_decl(name) {
let (call, span, err) = self.parse_internal_call(spans, decl_id);
( (
Expression { Expression {
expr: Expr::Call(Box::new(call)), expr: Expr::Call(call),
//ty: Type::Unknown, span,
span: span(spans),
}, },
error, err,
) )
} else { } else {
self.parse_external_call(spans) self.parse_external_call(spans)
@ -578,6 +608,36 @@ impl ParserWorkingSet {
} }
} }
pub fn parse_variable_expr(&mut self, span: Span) -> (Expression, Option<ParseError>) {
let (id, err) = self.parse_variable(span);
if err.is_none() {
if let Some(id) = id {
(
Expression {
expr: Expr::Var(id),
span,
},
None,
)
} else {
let name = self.get_span_contents(span).to_vec();
// this seems okay to set it to unknown here, but we should double-check
let id = self.add_variable(name, Type::Unknown);
(
Expression {
expr: Expr::Var(id),
span,
},
None,
)
}
} else {
(garbage(span), err)
}
}
pub fn parse_full_column_path(&mut self, span: Span) -> (Expression, Option<ParseError>) { pub fn parse_full_column_path(&mut self, span: Span) -> (Expression, Option<ParseError>) {
// FIXME: assume for now a paren expr, but needs more // FIXME: assume for now a paren expr, but needs more
let bytes = self.get_span_contents(span); let bytes = self.get_span_contents(span);
@ -809,7 +869,9 @@ impl ParserWorkingSet {
shape: SyntaxShape, shape: SyntaxShape,
) -> (Expression, Option<ParseError>) { ) -> (Expression, Option<ParseError>) {
let bytes = self.get_span_contents(span); let bytes = self.get_span_contents(span);
if bytes.starts_with(b"$") { if shape == SyntaxShape::Variable {
return self.parse_variable_expr(span);
} else if bytes.starts_with(b"$") {
return self.parse_dollar_expr(span); return self.parse_dollar_expr(span);
} else if bytes.starts_with(b"(") { } else if bytes.starts_with(b"(") {
return self.parse_full_column_path(span); return self.parse_full_column_path(span);
@ -873,7 +935,9 @@ impl ParserWorkingSet {
) )
} }
} }
SyntaxShape::String => self.parse_string(span), SyntaxShape::String | SyntaxShape::GlobPattern | SyntaxShape::FilePath => {
self.parse_string(span)
}
SyntaxShape::Block => self.parse_block_expression(span), SyntaxShape::Block => self.parse_block_expression(span),
SyntaxShape::Any => { SyntaxShape::Any => {
let shapes = vec![ let shapes = vec![
@ -1072,6 +1136,43 @@ impl ParserWorkingSet {
} }
pub fn parse_let(&mut self, spans: &[Span]) -> (Statement, Option<ParseError>) { pub fn parse_let(&mut self, spans: &[Span]) -> (Statement, Option<ParseError>) {
if let Some(decl_id) = self.find_decl(b"let") {
let (call, call_span, err) = self.parse_internal_call(spans, decl_id);
if err.is_some() {
return (
Statement::Expression(Expression {
expr: Expr::Call(call),
span: call_span,
}),
err,
);
} else if let Expression {
expr: Expr::Var(var_id),
..
} = &call.positional[0]
{
return (
Statement::VarDecl(VarDecl {
var_id: *var_id,
expression: call.positional[2].clone(),
}),
None,
);
}
}
(
Statement::Expression(Expression {
expr: Expr::Garbage,
span: span(spans),
}),
Some(ParseError::UnknownState(
"internal error: let statement unparseable".into(),
span(spans),
)),
)
/*
let mut error = None; let mut error = None;
if spans.len() >= 4 && self.parse_keyword(spans[0], b"let").is_none() { if spans.len() >= 4 && self.parse_keyword(spans[0], b"let").is_none() {
let (_, err) = self.parse_variable(spans[1]); let (_, err) = self.parse_variable(spans[1]);
@ -1094,6 +1195,7 @@ impl ParserWorkingSet {
Some(ParseError::Mismatch("let".into(), span)), Some(ParseError::Mismatch("let".into(), span)),
) )
} }
*/
} }
pub fn parse_statement(&mut self, spans: &[Span]) -> (Statement, Option<ParseError>) { pub fn parse_statement(&mut self, spans: &[Span]) -> (Statement, Option<ParseError>) {